package datasource_bak

import (
	"errors"
	"fmt"
	"gitee.com/kristas/booting-go/framework/common/util/ccmap"
	"gitee.com/kristas/booting-go/framework/common/util/reflectx"
	"gitee.com/kristas/booting-go/framework/core/log"
	"gitee.com/kristas/booting-go/framework/core/statement/types"
	"gitee.com/kristas/booting-go/framework/data"
	"gitee.com/kristas/booting-go/framework/data/tunnel_client/ssh"
	"gitee.com/kristas/booting-go/framework/data/tunnel_client/tcp"
	"github.com/modern-go/concurrent"
	"reflect"
	"sync"
)

var (
	tcpInit     sync.Once
	sshInit     sync.Once
	typeFactory = ccmap.NewConcurrentMap()
)

type Starter struct {
	types.Component
	Config             ssh.Config        `value:"ssh"`
	Log                log.Logger        `wire:""`
	Datasource         []Datasource      `wire:""`
	Repos              []data.Repository `wire:""`
	Upgraders          []ORMUpgrader     `wire:""`
	TCPDialer          *tcp.Dialer
	SSHDialer          *ssh.Dialer
	wrapper            *concurrent.Map
	upgraderTypeMapper map[reflect.Type]ORMUpgrader
}

func NewStarter() *Starter {
	return &Starter{
		wrapper:            concurrent.NewMap(),
		upgraderTypeMapper: make(map[reflect.Type]ORMUpgrader),
	}
}

func (s *Starter) Init() error {
	s.initDatasourceWrapper()
	s.initUpgraders()
	for i := range s.Repos {
		repo := s.Repos[i]
		err := reflectx.ForEachField(repo, true, func(field reflect.StructField, value reflect.Value) error {
			if tag, ok := field.Tag.Lookup("db"); ok {
				db, ok := LoadByType(field.Type)
				if !ok {
					upgrader := s.upgraderTypeMapper[field.Type]
					if upgrader == nil {
						return errors.New(fmt.Sprintf("upgrader for [%s] not found", field.Type))
					}
					wrapper, ok := s.wrapper.Load(tag)
					if !ok {
						return errors.New(fmt.Sprintf("Datasource [%s] in TAG not found", tag))
					}
					var err error
					db, err = upgrader.Upgrade(wrapper.(Wrapper))
					if err != nil {
						return err
					}
					SaveByType(field.Type, db)
				}
				value.Set(reflect.ValueOf(db))
			}
			return nil
		})
		if err != nil {
			return err
		}
	}
	return nil
}

func (s *Starter) initDatasourceWrapper() {
	for i := range s.Datasource {
		ds := s.Datasource[i]
		var wrapper Wrapper
		switch ds.GetTunnel() {
		case "ssh":
			wrapper = s.buildSSHConnection(ds)
		default:
			wrapper = s.buildTCPConnection(ds)
		}
		s.wrapper.Store(wrapper.Configure().Tag(), wrapper)
		//Save(wrapper)
	}
}

func (s *Starter) initUpgraders() {
	for i := range s.Upgraders {
		ug := s.Upgraders[i]
		s.upgraderTypeMapper[ug.UpgradeType()] = ug
	}
}

func (s *Starter) buildTCPConnection(configure Datasource) Wrapper {
	tcpInit.Do(func() {
		s.TCPDialer = tcp.NewDialer()
	})
	return s.buildDatasourceWrapper(configure)
}

func (s *Starter) buildSSHConnection(configure Datasource) Wrapper {
	sshInit.Do(func() {
		client := ssh.BuildSSHClient(s.Config)
		s.SSHDialer = ssh.NewDialer(client)
	})
	return s.buildDatasourceWrapper(configure)
}

func (s *Starter) buildDatasourceWrapper(configure Datasource) Wrapper {
	handler, ok := typeFactory.Get(configure.GetType()).(DataBaseConnectionHandler)
	if !ok {
		s.Log.Panic("datasource type: %s not support", configure.GetType())
	}
	db, err := handler(s, configure)
	if err != nil {
		s.Log.Panic(err.Error())
	}
	return newDatasourceWrapper(db, configure)
}

func UseDataBaseHandler(typ string, handler DataBaseConnectionHandler) {
	typeFactory.Set(typ, handler)
}
